use std::collections::HashMap;

use aqua::{convert::Responser, http::HttpResponse, server::prelude::*};
use serde::Serialize;
use utils::render_template_with_vars;

use crate::blog::{BlogState, Post};

const HOME_PATH: &str = "/home";

#[derive(Serialize, Responser)]
pub enum BlogError {
    #[response(state = 404, content_type = "json")]
    PostNotFound
}

#[get(path = "/home", state = state)]
async fn home(state: HttpState<BlogState>) -> Html<String> {
    let state = state.read().await;
    let posts = state.get_published();

    let mut html = String::new();
    html.push_str("<html><head><title>My Blog</title></head><body>");
    html.push_str("<h1>My Blog</h1><ul>");

    for post in posts {
        html.push_str(&format!(
            "<li><a href=\"/post/{}\">{}</a></li>",
            post.slug, post.title
        ));
    } 

    html.push_str("</ul></body></html>");

    Html(html)
}

#[get(path = "/post/<slug>", state = state)]
async fn show_post(slug: String, state: HttpState<BlogState>) -> Html<String> {
    let state = state.read().await;

    if let Some(post) = state.find_by_slug(&slug) {
        let html = format!(
            "<html>
                <head>
                    <meta charset=\"UTF-8\">
                    <title>{title}</title>
                </head>
                <body>
                    <h1>{title}</h1>
                    <p>{content}</p>
                    <a href=\"{HOME_PATH}\">Back to home</a>
                </body>
            </html>",
            title = post.title,
            content = post.content
        );
        Html(html)
    } else {
        let html = format!(
            "<html>
                <head><title>404 Not Found</title></head>
                <body>
                    <h1>404 - Post Not Found</h1>
                    <p>No post found for slug: {}</p>
                    <a href=\"/\">Back to home</a>
                </body>
            </html>",
            slug
        );
        Html(html)
    }
}

#[get(path = "/admin", state = state)]
async fn admin_dashboard(state: HttpState<BlogState>) -> Html<String> {
    let state = state.read().await;

    let mut html = String::new();
    html.push_str("<html><head><meta charset=\"UTF-8\"><title>后台管理</title></head><body>");
    html.push_str("<h1>后台文章管理</h1>");
    html.push_str("<table border=\"1\" cellpadding=\"8\"><tr><th>标题</th><th>状态</th><th>操作</th></tr>");

    for post in state.posts.iter() {
        let status = if post.published { "已发布" } else { "草稿" };
        html.push_str(&format!(
            "<tr>
                <td>{}</td>
                <td>{}</td>
                <td>
                    <a href=\"/post/{}\" target=\"_blank\">查看</a> |
                    <a href=\"#\">编辑</a>
                </td>
            </tr>",
            post.title,
            status,
            post.slug 
        ));
    }

    html.push_str("</table>");
    html.push_str("</body></html>");

    Html(html)
}

#[get(path = "/admin/new")]
fn new_post_page() -> Result<Html<String>, ServerResourceError> {
    render_template("new_post.html")
}

#[post(path = "/admin/new", body = post, state = state)]
async fn create_post(Json(post): Json<Post>, state: HttpState<BlogState>) -> HttpResponse {
    let mut state = state.write().await;
    state.add_post(post);
    HttpResponse::redirect("/admin")
}

#[get(path = "/admin/edit/<slug>", state = state)]
async fn edit_page(slug: String, state: HttpState<BlogState>) -> Result<Html<String>, HttpResponse> {
    let state = state.read().await;
    let post = state.posts.iter().find(|p| p.slug == slug)
        .ok_or_else(|| BlogError::PostNotFound.into_response())?;

    let mut vars = HashMap::new();
    vars.insert("title", post.title.clone());
    vars.insert("slug", post.slug.clone());
    vars.insert("content", post.content.clone());
    vars.insert("published", post.published.to_string());

    render_template_with_vars("edit_post.html", &vars).map_err(|e| e.into_response())
}

#[post(path = "/admin/edit/<slug>", body = modify_post, state = state)]
async fn edit_submit(slug: String, Json(modify_post): Json<Post>, state: HttpState<BlogState>) -> HttpResponse {
    let mut state = state.write().await;

    if let Some(post) = state.posts.iter_mut().find(|p| p.slug == slug) {
        post.title = modify_post.title;
        post.content = modify_post.content;
        post.published = modify_post.published;
    }

    HttpResponse::redirect("/admin")
}